#include "AnimatedModel.h"

//Include assimp headers (c++)
#include <assimp\Importer.hpp>
#include <assimp\types.h>
#include <assimp\postprocess.h>
#include <assimp\scene.h>
#include <assimp\mesh.h>
#include <assimp\material.h>
#include <assimp\color4.inl>
#include <assimp\vector3.inl>
#include <assimp\vector2.inl>
#include <assimp\matrix4x4.inl>

//Include animation effects 
#include "TileForwardAnimSolidColourFX.h"
#include "TileForwardAnimNormalMapFX.h"
#include "TileForwardAnimTextureMapFX.h"

//Function which converts an aiMatrix4x4t<float> to 
//XMFLOAT4X4 (XNA Maths)
XMFLOAT4X4 ToXMFLOAT4X4(aiMatrix4x4 m)
{
	XMFLOAT4X4 x;

	//m = m.Transpose();

    x.m[0][0] = m.a1; x.m[0][1] = m.a2; x.m[0][2] = m.a3; x.m[0][3] = m.a4;
    x.m[1][0] = m.b1; x.m[1][1] = m.b2; x.m[1][2] = m.b3; x.m[1][3] = m.b4;
    x.m[2][0] = m.c1; x.m[2][1] = m.c2; x.m[2][2] = m.c3; x.m[2][3] = m.c4;
    x.m[3][0] = m.d1; x.m[3][1] = m.d2; x.m[3][2] = m.d3; x.m[3][3] = m.d4;

	return x;
}

XMMATRIX ToXMMATRIX(aiMatrix3x3 m)
{
	XMMATRIX x;

	//m = m.Transpose();

    x.m[0][0] = m.a1; x.m[0][1] = m.a2; x.m[0][2] = m.a3; x.m[0][3] = 0.0f;
    x.m[1][0] = m.b1; x.m[1][1] = m.b2; x.m[1][2] = m.b3; x.m[1][3] = 0.0f;
    x.m[2][0] = m.c1; x.m[2][1] = m.c2; x.m[2][2] = m.c3; x.m[2][3] = 0.0f;
    x.m[3][0] = 0.0f; x.m[3][1] = 0.0f; x.m[3][2] = 0.0f; x.m[3][3] = 1.0f;

	return x;
}

//Function which converts an aiVector3 to XMFLOAT3
XMFLOAT3 ToXMFLOAT3(aiVector3D& v)
{
	XMFLOAT3 x;

	x.x = v.x;
	x.y = v.y;
	x.z = v.z;

	return x;
}

//Function which converts an aiQuaternion to XMFLOAT4
XMFLOAT4 ToXMFLOAT4(aiQuaternion& q)
{
	XMFLOAT4 x;

	x.x = q.x;
	x.y = q.y;
	x.z = q.z;
	x.w = q.w;

	return x;
}

AnimatedModel::AnimatedModel()
{
	//Init world matrix
	position = XMFLOAT3(0.0f, 0.0f, 0.0f);
	rotation = XMFLOAT3(0.0f, 0.0f, 0.0f);
	scaling = XMFLOAT3(1.0f, 1.0f, 1.0f);
	CreateWorldMatrix();

	//No bounding box initially. 
	aabbL.Center = XMFLOAT3(0.0f, 0.0f, 0.0f);
	aabbL.Extents = XMFLOAT3(0.0f, 0.0f, 0.0f);

	//Init total draw count (Triangle count in our case since
	//we do gen index buffers)
	totalDrawCount = 0;

	//Initially set animation is the first loaded. 
	currentActiveAnimation = 0;

	//Clear vectors for good measure. 
	subModels.clear();
	bones.clear();
	boneHierarchy.clear();
	animationClips.clear();

	//Init finalTransformArr var to 0. 
	finalTransformArr = NULL;

	//Animation has not begun playing yet. 
	totalRunTimeOfAnimation = 0.0;

	//Will cast shadows. 
	isShadowCaster = true;

	//Not inited yet since no model data has been loaded. 
	isInit = false;
}

AnimatedModel::~AnimatedModel()
{
	//Delete the system copy of verticies
	systemVertexCopyArr.clear();

	//Delete submodels. 
	for (unsigned i = 0; i < subModels.size(); i++)
		SafeDelete(subModels[i]);

	subModels.clear();

	//Clear animation vectors. 
	bones.clear();
	boneHierarchy.clear();
	animationClips.clear();

	//Delete the finalTransform array.
	if (finalTransformArr)
		delete []finalTransformArr;
}

void AnimatedModel::SetSubmodelsWithSameMaterial(Material& material)
{
	for (unsigned i = 0; i < subModels.size(); i++)
	{
		subModels[i]->Mat = material;
		if (material.Diffuse.w < 1.0f)
			subModels[i]->IsOpaque = false;
		else
			subModels[i]->IsOpaque = true;
	}
}

void AnimatedModel::SetSubModelMaterial(unsigned index, Material& material)
{
	if (index < subModels.size())
	{
		subModels[index]->Mat = material;
		if (material.Diffuse.w < 1.0f)
			subModels[index]->IsOpaque = false;
		else
			subModels[index]->IsOpaque = true;
	}
	else
		OutputDebugStringW(L"Error - Trying to update submodels material. Index out of bounds\n");
}

void AnimatedModel::SetSubmodelsWithSameReflectionData(XMFLOAT3 &reflectivity)
{
	XMFLOAT4 r = XMFLOAT4(reflectivity.x, reflectivity.y, reflectivity.z, 1.0f);
	for (unsigned i = 0; i < subModels.size(); i++)
		subModels[i]->Mat.Reflection = r;
}

void AnimatedModel::SetWorldPosition(XMFLOAT3 pos)
{
	//store position
	position = pos;

	//Create world matrix.
	CreateWorldMatrix();
}

void AnimatedModel::SetWorldRotation(XMFLOAT3 rot)
{
	//store rotation
	rotation = rot;
	
	//Create world matrix.
	CreateWorldMatrix();
}

void AnimatedModel::SetWorldScale(XMFLOAT3 scale)
{
	//store scaling factor
	scaling = scale;
	
	//Create world matrix.
	CreateWorldMatrix();
}

void AnimatedModel::SetWorldMatrix(XMFLOAT3 pos, XMFLOAT3 rot, XMFLOAT3 scale)
{
	//Store pos, rot and scale
	position = pos;
	rotation = rot;
	scaling = scale;

	//Create world matrrix.
	CreateWorldMatrix();
}

void AnimatedModel::SetActiveAnimation(std::string& animationName)
{
	//Ensure we have loaded a model. 
	if (isInit)
	{
		//Loop through AnimationClips and find clip entry which has the same name
		//as the string passed in. 
		std::vector<AnimationClip>::iterator it;
		it = animationClips.begin(); 
		//Loop counter - will represent the index
		//in to animationClips of the animation clip we have found. 
		unsigned index = 0;
		while (it != animationClips.end())
		{
			if (it->ClipName.compare(animationName) == 0)
			{
				//Match found - update the current active animation variable. 
				currentActiveAnimation = index;

				//Reset animation run timer. 
				totalRunTimeOfAnimation = 0.0;

				//Early return
				return;
			}
			else
			{
				//Next clip to test
				it++;
				//Increment index
				index++;
			}
		}

		//If we reach here, no animation clip found.
		OutputDebugStringW(L"AnimatedModel Error - No AnimationClip found for string: ");
		OutputDebugStringA(animationName.c_str());
		OutputDebugStringW(L"\n");
		while(1){}
	}
	else
	{
		OutputDebugStringW(L"AnimatedModel Error - Can not set active animation (0) - isInit == false\n");
		while(1){}
	}
}

void AnimatedModel::SetActiveAnimation(unsigned animationIndex)
{
	if (isInit)
	{
		//If animationIndex >= size of the animation clip vector, then
		//we know this animation can not exist. 
		if (animationIndex < animationClips.size())
		{
			//Update current active animation variable. 
			currentActiveAnimation = animationIndex;
			
			//Reset animation run timer. 
			totalRunTimeOfAnimation = 0.0;

			//Early return
			return;
		}
		else
		{
			//Error - Passed in index is too high. Animation can not exist. 
			OutputDebugStringW(L"AnimatedModel Error - No AnimationClip found for index: ");
			std::stringstream ss;
			ss << animationIndex;
			OutputDebugStringA(ss.str().c_str());
			OutputDebugStringW(L"\n");
			while(1){}
		}
	}
	else
	{
		OutputDebugStringW(L"AnimatedModel Error - Can not set active animation (1) - isInit == false\n");
		while(1){}
	}
}

void AnimatedModel::Update(float delta, bool shouldLoop, bool riggedPose)
{
	//Ensure that we have an inited model. 
	if (isInit)
	{
		//Update the animation run time. 
		totalRunTimeOfAnimation+=(double)delta; 

		if (shouldLoop)
		{
			//if this is a looping animation, reset the timer if it exceeds the maximum
			//for this animation
			if (totalRunTimeOfAnimation > animationClips[currentActiveAnimation].AnimationTime)
				totalRunTimeOfAnimation = 0.0;
		}
		else
		{
			//Else, is not a looping animation. Once we have played the animation, 
			//then we will have the last final transforms so return early. 
			if (totalRunTimeOfAnimation > animationClips[currentActiveAnimation].AnimationTime)
			{
				totalRunTimeOfAnimation-=(double)delta;
				return;
			}
		}

		//Generate final transformation matricies. 
		GenerateFinalTransforms(riggedPose);
	}
	else
		OutputDebugStringW(L"AnimatedModel Warning - Can not Update() - isInit == false\n");
}

void AnimatedModel::CreateWorldMatrix()
{
	//Create world matrix. 
	XMMATRIX trans = XMMatrixTranslation(position.x, position.y, position.z);
	XMMATRIX rotationX = XMMatrixRotationX(rotation.x);
	XMMATRIX rotationY = XMMatrixRotationY(rotation.y);
	XMMATRIX rotationZ = XMMatrixRotationZ(rotation.z);
	XMMATRIX rotationM = (rotationX * rotationY) * rotationZ;
	XMMATRIX scaleM = XMMatrixScaling(scaling.x, scaling.y, scaling.z);
	XMMATRIX world = (scaleM * rotationM) * trans;

	//Store in to newMaterial
	XMStoreFloat4x4(&worldMatrix, world);
}

void AnimatedModel::CreateAnimatedModelThroughAssimp(ID3D11Device* device, 
	std::string& file, std::string& textureDirectory, 		
	bool shouldMakeLeftHanded, bool shouldFlipUV, bool shouldFlipWinding,
	bool shouldFixInwardFacingNormals)
{
	if (!isInit)
	{
		OutputDebugStringW(L"Loading AnimatedModel through Assimp: ");
		OutputDebugStringA(file.c_str());
		OutputDebugStringW(L"\n");

		//Construct an assimp importer instance - imports
		//models to an aiScene which contains the mesh data for
		//the model. 
		Assimp::Importer asImp;

		//Load the file and check to see if it has animation data. If it
		//doesnt, this is an error so print a message and enter infinite
		//loop. 
		//
		//First, select which post processes we want to apply to the model - This can slow
		//model loading down, however it should be faster to render - Plus, we can right all sorts
		//of poor models. . 
		unsigned int ppFlags = 0;
		ppFlags = aiProcess_GenSmoothNormals					 //If normals are not loaded, generate them.
			| aiProcess_CalcTangentSpace						 //Generates tangent vectors. 
			| aiProcess_JoinIdenticalVertices					 //When used, we need to create an index buffer.
			| aiProcess_Triangulate								 //Triangulates quads automatically. 
			| aiProcess_GenUVCoords								 //Gens tex coords if not present (I think...)
			| aiProcess_OptimizeMeshes							 //Aims to reduce draw calls - which is good. 
			| aiProcess_SplitLargeMeshes						 //Only when required (On very large meshes)
			| aiProcess_LimitBoneWeights;						 //Limits the number of bones that effect a single vertex to 4. 

		//Optional flags.
		if (shouldMakeLeftHanded)
			ppFlags |= aiProcess_MakeLeftHanded;				 //If model was built in right hand system
		if (shouldFlipUV)
			ppFlags |=  aiProcess_FlipUVs;						 //If UV (0,0) is bottom left
		if (shouldFlipWinding)
			ppFlags |= aiProcess_FlipWindingOrder;				 //Flips from CCW to CW or CW to CCW. 
		if (shouldFixInwardFacingNormals)
			ppFlags |= aiProcess_FixInfacingNormals;			 //Finds inward facing normals and flips - Often not needed.               

		//Load model from file. Stored in aiScene. 
		const aiScene* scene = asImp.ReadFile(file, ppFlags);

		//if scene is NULL, an error occured. 
		if (!scene)
		{
			OutputDebugStringW(L"Static Model Error - Error parsing: ");
			OutputDebugStringA(file.c_str());
			OutputDebugStringW(L". Error String: ");
			OutputDebugStringA(asImp.GetErrorString());
			OutputDebugStringW(L"\n");
			while(1){}
		}

		//If the scene has animations, print warning.
		if (!scene->HasAnimations())
		{
			OutputDebugStringW(L"AnimatedModel Error - Model does not have animation data.\n");
			OutputDebugStringW(L"Use StaticModel (Or StaticInstancedModel) for this model.\n");
			OutputDebugStringW(L"Entering infinite loop...\n");
			while(1){}
		}

		//Extract all bones - these can influence the verticies.. 
		LoadBones(scene);

		//If the number of bones in this skeleton is more than the maximum
		//allowed, then this model can not be supported
		if (bones.size() > MAX_NUMBER_OF_BONES_PER_SKELETON)
		{
			OutputDebugStringW(L"AnimatedModel Error - Model contains too many bones and not currently supported\n");
			while(1){}
		}

		//Resize boneInfo size - this holds pre XNA converted final transforms. 
		boneInfo.resize(bones.size());

		//Etracts the bone hierarchy. Also extracts the to parent matrix. We use
		//this to generate a Final Transformation matrix per bone. Note, the hierarchy
		//from Assimp can contain nodes which do not corrospond to a bone - hence the need
		//for a seperate vector of this data. 
		LoadBoneHierarchy(scene, false);


		//Extract animation clips and key frame data. AFTER LoadBones() and LoadBoneHierarchy()
		LoadAnimationClips(scene);

		//Allocate enough memory for the finalTransform matricies - One per bone. Init
		//to identity. 
		if (!finalTransformArr)
		{
			finalTransformArr = new XMFLOAT4X4[boneInfo.size()];
			XMMATRIX id = XMMatrixIdentity();
			for (unsigned i = 0; i < boneInfo.size(); i++)
				XMStoreFloat4x4(&finalTransformArr[i], id);
		}

		//Loop through meshes. We create a single submodel per
		//mesh. This loads our vertex data including the vertex weights and 
		//vertex bone indicies. 
		for (unsigned i = 0; i < scene->mNumMeshes; i++)
			LoadSubModelThroughAssimp(device, scene, i, textureDirectory, false);

		//Once done, create an AABB for this model for use with the frustum culling
		//algorithm. Note that this will doesnt take in to account animations so we will
		//get a lot of models which pass the frustum test yet are not on screen. 
		CreateAABB();

		//Once we are done, set the init flag to true.
		isInit = true;
	}
	else
		OutputDebugStringW(L"AnimatedModel Error - Instance already inited\n");
}

void AnimatedModel::LoadBones(const aiScene* scene)
{
	//Extract all bones. 
	for (unsigned i = 0; i < scene->mNumMeshes; i++)
	{
		//Get this mesh
		aiMesh* m = scene->mMeshes[i];
		for (unsigned j = 0; j < m->mNumBones; j++)
		{
			//Get this bone
			aiBone* b = m->mBones[j];

			//Using this bone, fillot a bone data structure. At this time, we can
			//only extract the name and offset matrix. We need to use the name
			//to ensure that we are only adding unique bones to the vector. 
			Bone boneToAdd;
			boneToAdd.Name = b->mName.data;
			boneToAdd.OffsetMatrix = Matrix4f(b->mOffsetMatrix);
			boneToAdd.BoneIndex = bones.size();
				
			//Ensure this is a unqiue bone. 
			if (IsBoneUnique(&boneToAdd))
				bones.push_back(boneToAdd);
		}
	}
}

void AnimatedModel::LoadAnimationClips(const aiScene* scene)
{
	for (unsigned i = 0; i < scene->mNumAnimations; i++)
	{
		//Get pointer to this aiAnimation
		aiAnimation* anim = scene->mAnimations[i];

		//Fillout animation clip for the new animation
		AnimationClip newAnim;
		newAnim.ClipName = anim->mName.data;
		newAnim.DurationTicks = anim->mDuration;
		newAnim.TickRate = anim->mTicksPerSecond;
		newAnim.AnimationTime = newAnim.DurationTicks / newAnim.TickRate;

		//Resize the BoneAnimation data - will match the number of bones nodes in
		//the skeleton. Thereofre, we have one BoneAnimation entry per bone. This 
		//BoneAnimation (IE bone) entry can have n number of key frames which
		//we will load after. 
		newAnim.BonesAnimations.resize(boneHierarchy.size());

		//Loop through this animations chanels. A chanel
		//contains all the keyframe data for a given bone
		//node. 
		for (unsigned j = 0; j < anim->mNumChannels; j++)
		{
			//Get this chanel. 
			aiNodeAnim* na = anim->mChannels[j];
			//What bone node does this contain data for - use this to access
			//the BonesAnimation vector
			int index = ReturnIndexOfBoneNode(na->mNodeName.data);

			//Resize key frame vectors. 
			newAnim.BonesAnimations[index].Trans.resize(na->mNumPositionKeys);
			newAnim.BonesAnimations[index].Rots.resize(na->mNumRotationKeys);
			newAnim.BonesAnimations[index].Scales.resize(na->mNumScalingKeys);

			//Fillout translation key frames. 
			for (unsigned t = 0; t < na->mNumPositionKeys; t++)
			{
				//Get the aiVectorKey which contains time and a vector
				//keyframe data.
				aiVectorKey* key = &na->mPositionKeys[t];

				//Set data
				newAnim.BonesAnimations[index].Trans[t].Time = key->mTime;
				newAnim.BonesAnimations[index].Trans[t].Value = key->mValue;
			}

			//Fillout rotation key frames
			for (unsigned r = 0; r < na->mNumRotationKeys; r++)
			{
				//Get aiQuaternionKey which contains a time and a quaternion
				//representing a rotation key frame
				aiQuatKey* key = &na->mRotationKeys[r];

				//Set data
				newAnim.BonesAnimations[index].Rots[r].Time = key->mTime;
				newAnim.BonesAnimations[index].Rots[r].Value = key->mValue;
			}

			//And finally, the scale keys. 
			for (unsigned s = 0; s < na->mNumScalingKeys; s++)
			{
				//Get scaling key. Like above, contaisn a time and scaling
				//factor for a keyframe. 
				aiVectorKey* key = &na->mScalingKeys[s];

				//Set data
				newAnim.BonesAnimations[index].Scales[s].Time = key->mTime;
				newAnim.BonesAnimations[index].Scales[s].Value = key->mValue;
			}
		}

		//Push on to vector of anim clips
		animationClips.push_back(newAnim);
	}
}

void AnimatedModel::LoadBoneHierarchy(const aiScene* scene, bool shouldOutputNodeHierarchy)
{
	//Get the Root node.
	aiNode* root = scene->mRootNode;

	//Add root bone to hierarchy.
	BoneNode b;
	b.Name = root->mName.data;
	b.ParentIndex = -1; //This is the root node. 
	b.ToParentMatrix = Matrix4f(root->mTransformation); //Global transform
	b.BoneIndex = ReturnIndexOfBone(b.Name);
	boneHierarchy.push_back(b);

	//Inverse and store the global transform matrix.
	globalInverseTransform = Matrix4f(root->mTransformation.Inverse());

	//Loop through the children of this root node. 
	for (unsigned i = 0; i < root->mNumChildren; i++)
		LoadBoneChild(root->mChildren[i]);

	//Traverse the bone hierarchy and set each nodes children. 
	for (unsigned i = 0; i < boneHierarchy.size(); i++)
	{
		BoneNode* bn = &boneHierarchy[i];
		if (bn->ParentIndex != -1)
			boneHierarchy[bn->ParentIndex].ChildNodes.push_back(&boneHierarchy[i]);
	}

	//Map bone to bone nodes. 
	for (unsigned i = 0; i < bones.size(); i++)
		bones[i].BoneNodeIndex = ReturnIndexOfBoneNode(bones[i].Name);

	//Output node hierarchy to the console. 
	if (shouldOutputNodeHierarchy)
	{
		for (unsigned i = 0; i < boneHierarchy.size(); i++)
		{
			OutputDebugStringW(L"Bone Node Index ");
			std::stringstream ss;
			ss << i;
			OutputDebugStringA(ss.str().c_str());

			OutputDebugStringW(L" Bone Index ");
			std::stringstream ss4;
			ss4 << boneHierarchy[i].BoneIndex;
			OutputDebugStringA(ss4.str().c_str());

			OutputDebugStringW(L" Bone Node Name ");
			std::stringstream ss3;
			ss3 << boneHierarchy[i].Name;
			OutputDebugStringA(ss3.str().c_str());

			OutputDebugStringW(L" To Parent Index: ");
			std::stringstream ss2; 
			ss2 << boneHierarchy[i].ParentIndex;
			OutputDebugStringA(ss2.str().c_str());

			std::stringstream ss6;
			ss6 << boneHierarchy[i].ChildNodes.size();
			OutputDebugStringW(L" Num Child Nodes : ");
			OutputDebugStringA(ss6.str().c_str());
			OutputDebugStringW(L"\n");
		}
	}
}

void AnimatedModel::LoadBoneChild(aiNode* child)
{
	//Fillout bone node struct for this (child) bone node)
	BoneNode bn;
	bn.BoneIndex = ReturnIndexOfBone(child->mName.data); //Does this node corrospond to a bone?
	bn.Name = child->mName.data; //Bone NODE name
	bn.ParentIndex = ReturnIndexOfBoneNode(child->mParent->mName.data); //Parent index. 
	bn.ToParentMatrix = Matrix4f(child->mTransformation); //ToParent matrix. 
	
	//Is this node unique?
	if (IsBoneNodeUnique(&bn))
		boneHierarchy.push_back(bn);

	//Loop through this nodes children and call this function again. 
	for (unsigned i = 0; i < child->mNumChildren; i++)
		LoadBoneChild(child->mChildren[i]);
}

void AnimatedModel::LoadSubModelThroughAssimp(ID3D11Device* device, const aiScene* scene, unsigned meshIndex, 
	std::string& textureDir, bool loadDispMap)
{
	//Submodel for this mesh and material. 
	SubModelData* subModel = new SubModelData();

	//Get ith mesh (Vertex/index data). 
	aiMesh* mesh = scene->mMeshes[meshIndex];
	//Get this meshes material index. We can use this to get the
	//aiMaterial struct for this submodel
	aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

	//Set SubModel Properties that are the same for all Assimp loaded
	//models. 
	XMStoreFloat4x4(&subModel->TexTrans, XMMatrixScaling(1.0f, 1.0f, 1.0f));
	subModel->StartDrawIndex = 0;
	//Initially false - Indeed, for the time being, we wont be loading
	//a height/displacement maps for animating models (They cause
	//wayy too many issues to bother. Not to mention, inefficient)
	subModel->IsUsingDisplacementMapping = false;
	subModel->IsUsingSeperateHeightMap = false;

	//Load material data.
	LoadAssimpMaterial(device, material, subModel, textureDir, loadDispMap);
	//Load vertex (And index) data.
	LoadAssimpVertexData(device, mesh, subModel);

	//Push submodel on to vector.
	subModels.push_back(subModel);
}

void AnimatedModel::LoadAssimpMaterial(ID3D11Device* device, aiMaterial* material, SubModelData* subModel, 
	std::string &textureDir, bool loadDispMap)
{
	//Load the material
	//
	//Get diffuse map path if one exists. Else its a flat colour. 
	if (material->GetTextureCount(aiTextureType_DIFFUSE) > 0)
	{
		//Get filename of the texture. 
		aiString path;
		if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
		{
			//Success - we have the texture file name - Concatinate with 
			//the passed in texture directory to get the full filepath
			std::string fullFilePath = textureDir + path.data;

			//Load texture map.
			LoadTexture(device, fullFilePath, &subModel->TextureMap);
		}
	}

	//Get normal map path if one exists. 
	if (material->GetTextureCount(aiTextureType_NORMALS) > 0)
	{
		//Get filename of the normal map (only the one supproted
		//at the moment)
		aiString path;
		if (material->GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
		{
			//Found the texture file name - Load it. 
			std::string fullFilePath = textureDir + path.data;

			//Load normal map
			LoadTexture(device, fullFilePath, &subModel->NormalMap);
		}
	}

	//Get height map (if one exists and option ebaled) - Can only be one of either
	//a displacement map or height map. (Or neither)
	if (loadDispMap)
	{
		if (material->GetTextureCount(aiTextureType_HEIGHT ) > 0)
		{
			//If one is found, we are using displacement mapping for this
			//model.
			subModel->IsUsingDisplacementMapping = true;
			subModel->IsUsingSeperateHeightMap = true;

			//Get filename of the height map.
			aiString path;
			if (material->GetTexture(aiTextureType_HEIGHT, 0, &path) == AI_SUCCESS)
			{
				//Found the disp map file name. Load
				std::string fullFilePath = textureDir + path.data;

				//Load disp map.
				LoadTexture(device, fullFilePath, &subModel->HeightMap);
			}
		}
		else if (material->GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
		{
			//If one is found, we are using displacement mapping for this
			//model.
			subModel->IsUsingDisplacementMapping = true;
			subModel->IsUsingSeperateHeightMap = true;

			//Get filename of the disp map.
			aiString path;
			if (material->GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
			{
				//Found the disp map file name. Load
				std::string fullFilePath = textureDir + path.data;

				//Load disp map.
				LoadTexture(device, fullFilePath, &subModel->HeightMap);
			}
		}
	}

	//Get lighting material data (reflection data). Material::Diffuse.A
	//is the opacity. If < 1.0, then we have a transparant Sub Model.
	aiColor3D d(1.f, 1.f, 1.f); //Diffuse
	float o = 1.0f;				//Diffuse.w
	aiColor3D s(1.f, 1.f, 1.f); //Spec
	float sh = 64.0f;			//Spec shinyness
	aiColor3D a(1.f, 1.f, 1.f); //Ambient
	aiColor3D ref(0.0f, 0.0f, 0.0f); //Reflectivity. 


	//Get material colours. 
	material->Get(AI_MATKEY_COLOR_DIFFUSE, d); 
	material->Get(AI_MATKEY_OPACITY, o);
	material->Get(AI_MATKEY_COLOR_SPECULAR, s);
	material->Get(AI_MATKEY_SHININESS_STRENGTH, sh);
	material->Get(AI_MATKEY_COLOR_AMBIENT, a);
	material->Get(AI_MATKEY_COLOR_REFLECTIVE, ref);

	//Flags to indicate if anything is (0,0,0). If all are false,
	//we use the default material. Else, use the ones loaded. 
	bool diffAv = true;
	bool specAv = true;
	bool ambAv = true;

	if (d.r == 0.0f && d.g == 0.0f && d.b == 0.0)
		diffAv = false;
	if (s.r == 0.0f && s.g == 0.0f && s.b == 0.0)
		specAv = false;
	if (a.r == 0.0f && a.g == 0.0f && a.b == 0.0)
		ambAv = false;

	//If one of the above is available, set material. Else
	//use default.
	if (diffAv || specAv || ambAv)
	{
		subModel->Mat.Diffuse.x = d.r;
		subModel->Mat.Diffuse.y = d.g;
		subModel->Mat.Diffuse.z = d.b;
		subModel->Mat.Diffuse.w = o;

		subModel->Mat.Specular.x = s.r;
		subModel->Mat.Specular.y = s.g;
		subModel->Mat.Specular.z = s.b;
		subModel->Mat.Specular.w = sh;

		subModel->Mat.Ambient.x = a.r;
		subModel->Mat.Ambient.y = a.g;
		subModel->Mat.Ambient.z = a.b;
		subModel->Mat.Ambient.w = 1.0f; //Not utilised. 
	}
	else
	{
		//OutputDebugStringW(L"Assimp Info: No reflectivity data detected. Using default\n");
		subModel->Mat = Material(); //Default material. 
	}

	//Set reflectivity data. 
	subModel->Mat.Reflection.x = ref.r;
	subModel->Mat.Reflection.y = ref.g;
	subModel->Mat.Reflection.z = ref.b;

	//Is transparant object if opacity < 1.0. Else,
	//is opaque. 
	if(subModel->Mat.Diffuse.w < 1.0f)
		subModel->IsOpaque = false;
	else
		subModel->IsOpaque = true;
}

void AnimatedModel::LoadAssimpVertexData(ID3D11Device* device, aiMesh* mesh, SubModelData* subModel)
{
	//This is called after we parse the material for this submodel. We therefore
	//know what textures this sub model requires - thus, we know what kind
	//of data we are extracting. 
	//
	//If the model has a displacement map / height map / normal map, we need tangent vectors. If 
	//we do not have a diffuse map to map, we just need pos and normals. Else its pos, norm, texC. 
	//
	//Per vertex structures.
	std::vector<TileForwardAnimSolidColourFX::PerVertex>solidColourVerticies;
	std::vector<TileForwardAnimTextureMapFX::PerVertex>texMapVerticies;
	std::vector<TileForwardAnimNormalMapFX::PerVertex>tangentVerticies; // Also disp mapping.

	//Enum - Tells us which vector above to write too. 
	enum Type
	{
		TYPE_SOLID_COLOUR = 0, //Pos/Norm
		TYPE_TEX_MAP_ONLY = 1, //Pos/Tex/Norm
		TYPE_TANGENT = 2 //Normal mapping / disp mapping. - Pos/Tex/Normal/Tang 
	};

	Type t;
	if (subModel->HeightMap)
		t = TYPE_TANGENT;
	else if (subModel->NormalMap)
		t = TYPE_TANGENT;
	else if (subModel->TextureMap)
		t = TYPE_TEX_MAP_ONLY;
	else
		t = TYPE_SOLID_COLOUR;

	//Loop through verticies for this mesh and extract in to (one of the...) above vectors.
	for (unsigned i = 0; i < mesh->mNumVertices; i++)
	{
		//Copy position to our system copy vertex vector.
		aiVector3D vP;
		vP = mesh->mVertices[i];
		systemVertexCopyArr.push_back(XMFLOAT3(vP.x, vP.y, vP.z));

		//Copy other data depending (normal etc)
		if (t == TYPE_SOLID_COLOUR)
		{
			//Vertex data
			TileForwardAnimSolidColourFX::PerVertex v;
			
			//Extract only normal data. 
			aiVector3D vN;
			vN = mesh->mNormals[i];

			//Copy Position and normal vector
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);

			//Push back on to vector. 
			solidColourVerticies.push_back(v);
		}
		else if (t == TYPE_TEX_MAP_ONLY)
		{
			//Vertex data
			TileForwardAnimTextureMapFX::PerVertex v;

			//Extract normal data and tex coord data
			aiVector3D vN = mesh->mNormals[i];
			aiVector3D vT = mesh->mTextureCoords[0][i];

			//Copy position, normal and tex data.
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);
			v.Tex = XMFLOAT2(vT.x, vT.y);

			//Push on to vector
			texMapVerticies.push_back(v);
		}
		else if (t == TYPE_TANGENT)
		{
			//Vertex data - Normal mapping / displacement mapping handled here.
			TileForwardAnimNormalMapFX::PerVertex v;

			//Extract normal, tex and tangent data.
			aiVector3D vN = mesh->mNormals[i];
			aiVector3D vT = mesh->mTextureCoords[0][i];
			aiVector3D vTng = mesh->mTangents[i];

			//Copy position, normal, tex and tangent data
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);
			v.Tex = XMFLOAT2(vT.x, vT.y);
			v.TangentL = XMFLOAT3(vTng.x, vTng.y, vTng.z);

			//Push on to vector
			tangentVerticies.push_back(v);
		}
	}

	//Extract per vertex bone infromation - bone indicies and their weights. Loop
	//through all verticies on by one.
	for (unsigned i = 0; i < mesh->mNumVertices; i++)
	{
		//(up to)4 bones influence this vertex. Find them and store them 
		//in to this vector
		std::vector<aiBone*>influencingBones;
		influencingBones.clear();

		//loop through all bones and find the (up to) 4 that effect
		//this vertex
		for (unsigned j = 0; j < mesh->mNumBones; j++)
		{
			//Get jth bone. 
			aiBone* b = mesh->mBones[j];

			//This bone influences k verticies. Loop through and see
			//if any of t5hese verticies matches the ith vertex. 
			for (unsigned k = 0; k < b->mNumWeights; k++)
			{
				aiVertexWeight w = b->mWeights[k];

				//if jth bone influences vertex i. Add to the vector. 
				if (w.mVertexId == i)
					influencingBones.push_back(b);
			}//k
		}//j

		//Ensure that only 4 bones effect this vertex - shows assertion
		//message if not (Contact me if this occurs as something
		//serious has occured - or the idiot programmer has commented
		//out the flag :/)
		assert(influencingBones.size() <= MAX_BONES_PER_VERTEX);

		//Once we have found the bones that effect this vertex, we can
		//add the data to our per vertex structures. 
		for (unsigned ty = 0; ty < influencingBones.size(); ty++)
		{
			//Get bone
			aiBone* b = influencingBones[ty];

			//Loop through this bones list of influences. Check to see
			//if it influences this vertex. 
			for (unsigned p = 0; p < b->mNumWeights; p++)
			{
				aiVertexWeight w = b->mWeights[p];
				if (w.mVertexId == i)
				{
					//Update ith per vertex structure with the bone index
					//and weights. 
					if (t == TYPE_SOLID_COLOUR)
					{
						solidColourVerticies[i].BoneIndicies[ty] = ReturnIndexOfBone(b->mName.data);
						if (ty < 3)
							solidColourVerticies[i].Weights[ty] = w.mWeight;
					}
					else if (t == TYPE_TEX_MAP_ONLY)
					{
						texMapVerticies[i].BoneIndicies[ty] = ReturnIndexOfBone(b->mName.data);
						if (ty < 3)
							texMapVerticies[i].Weights[ty] = w.mWeight;
					}
					else if (t == TYPE_TANGENT)
					{
						tangentVerticies[i].BoneIndicies[ty] = ReturnIndexOfBone(b->mName.data);
						if (ty < 3)
							tangentVerticies[i].Weights[ty] = w.mWeight;
					}
				}
			}//p
		}//ty
	}//i

	//After filling vectors, we need to create vertex buffer. 
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	if (t == TYPE_SOLID_COLOUR)
		bd.ByteWidth = sizeof(TileForwardAnimSolidColourFX::PerVertex) * mesh->mNumVertices;
	else if (t == TYPE_TEX_MAP_ONLY)
		bd.ByteWidth = sizeof(TileForwardAnimTextureMapFX::PerVertex) * mesh->mNumVertices;
	else if (t == TYPE_TANGENT)
		bd.ByteWidth = sizeof(TileForwardAnimNormalMapFX::PerVertex) * mesh->mNumVertices;

	//Init data - above. 
	D3D11_SUBRESOURCE_DATA sd;
	if (t == TYPE_SOLID_COLOUR)
		sd.pSysMem = &solidColourVerticies[0];
	else if (t == TYPE_TEX_MAP_ONLY)
		sd.pSysMem = &texMapVerticies[0];
	else if (t == TYPE_TANGENT)
		sd.pSysMem = &tangentVerticies[0];


	//Create buffer.
	HR(device->CreateBuffer(&bd, &sd, &subModel->vb));

	//Create index buffer. 
	std::vector<unsigned int>ind;
	for (unsigned i = 0; i < mesh->mNumFaces; i++)
	{
		//Get face.
		const aiFace &f = mesh->mFaces[i];

		//assert to ensure 3 indicies per face (triangle)
		assert(f.mNumIndices == 3);

		//Get (3) indicies and push on to vector. 
		ind.push_back(f.mIndices[0]);
		ind.push_back(f.mIndices[1]);
		ind.push_back(f.mIndices[2]);
	}

	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * ind.size();
	sd.pSysMem = &ind[0];

	//Create index buffer.
	HR(device->CreateBuffer(&bd, &sd, &subModel->ib));

	//Set submodel topology.
	if (subModel->IsUsingDisplacementMapping)
		subModel->Topology = D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
	else
		subModel->Topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

	//Set draw count for submodel.  
	subModel->DrawCount = ind.size();
	//Increment the total (Every sub model) draw count. 
	this->totalDrawCount += subModel->DrawCount;
}

std::wstring AnimatedModel::ToWS(std::string s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

void AnimatedModel::LoadTexture(ID3D11Device* device, std::string& file, ID3D11ShaderResourceView** srv)
{
	//We dont support .tga files (that everyone and their dog uses...), so if
	//we are using a .tga format, load the png version instead. Note, this 
	//does mean that you need to convert the file to PNG (Not JPEG as that
	//doesnt support transparancy where TGA does).
	//
	//Copy to intermediate string
	std::string f = file; 
	//Extract last three chars
	std::string fFormat = "   ";
	fFormat[2] = f[f.size()-1];
	fFormat[1] = f[f.size()-2];
	fFormat[0] = f[f.size()-3];

	//If is "tga", change to png
	if (fFormat.compare("tga") == 0)
	{
		//Remove the file format (do so untill we reach a '.')
		char c = ' ';
		while (c != '.')
		{
			f.pop_back();
			c = f[f.size()-1];
		}

		//Replace with png
		f.push_back('p');
		f.push_back('n');
		f.push_back('g');
	}

	//Convert to wstring - .c_str converts to LPCWSTR
	std::wstring fileWS = ToWS(f);

	//Load the texture map. 
	HR(D3DX11CreateShaderResourceViewFromFile(device, 
		fileWS.c_str(), 0,0, srv, 0));
}

bool AnimatedModel::IsBoneUnique(Bone* b)
{
	//Loop through bones vector
	std::vector<Bone>::iterator it;
	it = bones.begin(); 
	while (it != bones.end())
	{
		//Compare strings if they match, return false.  
		if (it->Name.compare(b->Name) == 0)
			return false;
		else
			//Next bone to test.
			it++;
	}

	//If we reach here, this bone is unique
	return true;
}

int AnimatedModel::ReturnIndexOfBone(std::string name)
{
	//Check 0th element first. 
	int ret = 0;
	
	//Loop through bomes. 
	std::vector<Bone>::iterator it;
	it = bones.begin(); 
	while (it != bones.end())
	{
		//Compare strings if they match, return false.  
		if (it->Name.compare(name) == 0)
			return ret;
		else
		{
			//Next bone to test in vector.
			it++;
			//Increment ret.
			ret++;
		}
	}

	//Return -1 indicating nothing found if we reach here. 
	return -1;
}

bool AnimatedModel::IsBoneNodeUnique(BoneNode* b)
{
	//Loop through bones vector
	std::vector<BoneNode>::iterator it;
	it = boneHierarchy.begin(); 
	while (it != boneHierarchy.end())
	{
		//Compare strings if they match, return false.  
		if (it->Name.compare(b->Name) == 0)
			return false;
		else
			//Next bone to test.
			it++;
	}

	//If we reach here, this bone is unique
	return true;
}
	
int AnimatedModel::ReturnIndexOfBoneNode(std::string name)
{
	//Check 0th element first. 
	int ret = 0;
	
	//Loop through bomes. 
	std::vector<BoneNode>::iterator it;
	it = boneHierarchy.begin(); 
	while (it != boneHierarchy.end())
	{
		//Compare strings if they match, return false.  
		if (it->Name.compare(name) == 0)
			return ret;
		else
		{
			//Next bone to test in vector.
			it++;
			//Increment ret.
			ret++;
		}
	}

	//Return -1 indicating nothing found if we reach here. 
	return -1;
}

void AnimatedModel::CreateAABB()
{
	//AABB - min and max xyz values. 
	XMFLOAT3 min = XMFLOAT3(+MathHelper::Infinity, +MathHelper::Infinity, +MathHelper::Infinity);
	XMFLOAT3 max = XMFLOAT3(-MathHelper::Infinity, -MathHelper::Infinity, -MathHelper::Infinity);

	XMVECTOR vMin = XMLoadFloat3(&min);
	XMVECTOR vMax = XMLoadFloat3(&max);

	//Loop through system copy of positions
	for (unsigned i = 0; i < systemVertexCopyArr.size(); i++)
	{
		//Load position in to a vector.
		XMVECTOR point = XMLoadFloat3(&systemVertexCopyArr[i]);

		//Update min/max
		vMin = XMVectorMin(vMin, point);
		vMax = XMVectorMax(vMax, point);
	}

	//Work out center point and the extents
	XMVECTOR center = 0.5f * (vMin + vMax);
	XMVECTOR extend = 0.5f * (vMax - vMin);

	//Convert to XMFLOAT3 format. 
	XMFLOAT3 fC;
	XMFLOAT3 fE;
	XMStoreFloat3(&fC, center);
	XMStoreFloat3(&fE, extend);

	//Scale up because this is an animating model. 
	XMFLOAT3 fE2;
	fE2.x = fE.x * 3.0f;
	fE2.y = fE.y * 3.0f;
	fE2.z = fE.z * 3.0f;

	//update AABB structure.
	aabbL.Center = fC;
	aabbL.Extents = fE2;
}

void AnimatedModel::GenerateFinalTransforms(bool riggedPose)
{
	//Identity matrix is passed as the toParentTranaform when we kick off the 
	//recusive funciton. 
	Matrix4f id;
	id.InitIdentity();

	//Work out how far along the animation we are. 
	float ticksPerSecond = (float)(animationClips[currentActiveAnimation].TickRate != 0 
		? animationClips[currentActiveAnimation].TickRate : 25.0f);
    float timeInTicks = (float)totalRunTimeOfAnimation * ticksPerSecond;
    float animationTime = fmod(timeInTicks, (float)animationClips[currentActiveAnimation].DurationTicks);

	//If not in rigged pose, generate final transforms. Else, identity matrix is inited
	//for all bones. The model will then be in bind pose (I think thats the term?)
	if (!riggedPose)
		TraverseNodes(animationTime, &boneHierarchy[0], 0, id);
	else
		for (UINT i = 0; i < boneInfo.size(); i++)
			boneInfo[i].FinalTransformation = id;

	//Convert from Matrix4f to XNA. 
	for (UINT i = 0; i < (UINT)bones.size(); i++)
		for (UINT r = 0; r < 4; r++)
			for (UINT c = 0; c < 4; c++)
				finalTransformArr[i].m[r][c] = boneInfo[i].FinalTransformation.m[c][r];
}

void AnimatedModel::TraverseNodes(float animationTime, BoneNode* node, 
	unsigned nodeIndex, const Matrix4f &toParentTransform)
{
	std::string nodeName(node->Name);   
	Matrix4f nodeTransformation(node->ToParentMatrix);
    
    if (ReturnIndexOfBone(nodeName) != -1) 
	{
        // Interpolate scaling and generate scaling transformation matrix
        aiVector3D scaling;
        CalcInterpolatedScaling(scaling, animationTime, nodeIndex);
        Matrix4f scalingM;
        scalingM.InitScaleTransform(scaling.x, scaling.y, scaling.z);
        
        // Interpolate rotation and generate rotation transformation matrix
        aiQuaternion rotationQ;
        CalcInterpolatedRotation(rotationQ, animationTime, nodeIndex);        
        Matrix4f rotationM = Matrix4f(rotationQ.GetMatrix());

        // Interpolate translation and generate translation transformation matrix
        aiVector3D translation;
        CalcInterpolatedPosition(translation, animationTime, nodeIndex);
        Matrix4f translationM;
        translationM.InitTranslationTransform(translation.x, translation.y, translation.z);
        
        // Combine the above transformations
        nodeTransformation = translationM * rotationM * scalingM;
    }
   
	//multiply the node transform (which may or may not include animation 
	//data with the toParent matrix and parents node transforms (this is
	//actually the toRoot matrix since we traverse the tree starting at the 
	//root bone)
    Matrix4f globalTransformation = toParentTransform * nodeTransformation;



    if (ReturnIndexOfBone(nodeName)!= -1)
	{
        uint boneIndex = ReturnIndexOfBone(nodeName);
        boneInfo[boneIndex].FinalTransformation = globalInverseTransform * 
			globalTransformation * bones[boneIndex].OffsetMatrix;
	}
    
	for (uint i = 0 ; i < node->ChildNodes.size() ; i++) 
		TraverseNodes(animationTime, node->ChildNodes[i], 
		ReturnIndexOfBoneNode(node->ChildNodes[i]->Name), globalTransformation);
}

void AnimatedModel::CalcInterpolatedScaling(aiVector3D& out, float animationTime, unsigned boneNodeIndex)
{
	if (animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales.size() == 1) 
	{
		out = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[0].Value;
        return;
    }

    UINT scalingIndex = FindScaling(animationTime, boneNodeIndex);
    UINT nextScalingIndex = (scalingIndex + 1);

	float iTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[scalingIndex].Time;
	float ippTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[nextScalingIndex].Time;

    float deltaTime = ippTime - iTime;
    float factor = (animationTime - iTime) / deltaTime;
    assert(factor >= 0.0f && factor <= 1.0f);
    
	const aiVector3D& start = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[scalingIndex].Value;
	const aiVector3D& end = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[nextScalingIndex].Value;
    aiVector3D delta = end - start;

    out = start + factor * delta;
}

void AnimatedModel::CalcInterpolatedRotation(aiQuaternion& out, float animationTime, unsigned boneNodeIndex)
{
	if (animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots.size() == 1) 
	{
		out = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[0].Value;
        return;
    }
    
    UINT rotationIndex = FindRotation(animationTime, boneNodeIndex);
    UINT nextRotationIndex = (rotationIndex + 1);

	float iTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[rotationIndex].Time;
	float ippTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[nextRotationIndex].Time;


    float deltaTime = ippTime - iTime;
    float factor = (animationTime - iTime) / deltaTime;
    assert(factor >= 0.0f && factor <= 1.0f);

	const aiQuaternion& startRotationQ = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[rotationIndex].Value;
    const aiQuaternion& endRotationQ   = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[nextRotationIndex].Value;    
    aiQuaternion::Interpolate(out, startRotationQ, endRotationQ, factor);
    out = out.Normalize();
}

void AnimatedModel::CalcInterpolatedPosition(aiVector3D& out, float animationTime, unsigned boneNodeIndex)
{
	if (animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans.size() == 1) 
	{
		out = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[0].Value;
        return;
    }
            
    UINT positionIndex = FindPosition(animationTime, boneNodeIndex);
    UINT nextPositionIndex = (positionIndex + 1);

	float iTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[positionIndex].Time;
	float ippTime = (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[nextPositionIndex].Time;

    float deltaTime = ippTime - iTime;
    float factor = (animationTime - iTime) / deltaTime;
    assert(factor >= 0.0f && factor <= 1.0f);
    
	const aiVector3D& start = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[positionIndex].Value;
    const aiVector3D& end = animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[nextPositionIndex].Value;
    aiVector3D delta = end - start;
   
	out = start + factor * delta;
}

UINT AnimatedModel::FindScaling(float animationTime, UINT boneNodeIndex)
{
    for (UINT i = 0 ; 
		i < animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales.size() - 1; 
		i++)
	{
		if (animationTime < 
			(float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Scales[i + 1].Time)
            return i;
    }
    return 0;
}

UINT AnimatedModel::FindRotation(float animationTime, UINT boneNodeIndex)
{
	for (UINT i = 0 ; 
		i < animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots.size() - 1;
		i++) 
	{
        if (animationTime < 
			(float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Rots[i+1].Time) 
            return i;
    }
 
    return 0;
}

UINT AnimatedModel::FindPosition(float animationTime, UINT boneNodeIndex)
{
	for (UINT i = 0 ;
		i < animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans.size() - 1 ;
		i++)
	{
       if (animationTime < 
		   (float)animationClips[currentActiveAnimation].BonesAnimations[boneNodeIndex].Trans[i+1].Time) 
            return i;
    }

    return 0;
}

XNA::AxisAlignedBox AnimatedModel::CalculateWorldSpaceAABB()
{
	//Calc world space AABB.
	XNA::AxisAlignedBox aabbW;

	//Convert from XMFLOAT3 to XMVECTOR
	XMVECTOR r = XMLoadFloat3(&rotation);
	XMVECTOR t = XMLoadFloat3(&position);

	//Do transform
	XNA::TransformAxisAlignedBox(&aabbW, &aabbL, scaling.x, r, t);

	return aabbW;
}